package com.suray.wcs.service.plc;

import com.suray.basic.wcs.path.core.Node;
import com.suray.basic.wcs.plc.pojo.oper.lifter.ForkLifterOper;
import com.suray.basic.wcs.plc.utils.*;
import com.suray.basic.wcs.utils.LoggerType;
import com.suray.basic.wcs.utils.LoggerUtil;
import com.suray.basic.wcs.utils.ModuleType;
import com.suray.basic.wcs.utils.WcsFuncUtil;

import java.io.IOException;
import java.util.*;

public class LifterTaskManage {

    private static Map<ForkLifterOper, LifterTaskManage> taskManageMap = new HashMap<>();

    private ForkLifterOper lifterOper;

    private LifterTaskContainer container;

    private LifterLocationInfo lastLocation;

    public void initLastLocation() {
        if (lifterOper.isReady() && !lifterOper.isRuning() && !lifterOper.isTasking() && !lifterOper.isTwoCarDriving()) {
            lastLocation = new LifterLocationInfo(lifterOper.getHoistCurrentLayer(), lifterOper.getHoistLayerType(),
                    LifterLocationType.parseLocal(lifterOper.getTwoCarLocal()));
        }
    }

    private LifterTaskManage(ForkLifterOper lifterOper) {
        this.lifterOper = lifterOper;

        container = new LifterTaskContainer(lifterOper.getDeviceName(), lifterOper);
    }

    public static synchronized LifterTaskManage getInstance(ForkLifterOper lifterOper) {
        LifterTaskManage taskManage = taskManageMap.get(lifterOper);
        if (taskManage == null) {
            taskManage = new LifterTaskManage(lifterOper);
            taskManageMap.put(lifterOper, taskManage);
        }

        return taskManage;
    }

    public void init() {
        container.init();
        initLastLocation();
    }

    public void changeLifterLayerNoCmd(int taskId, int layer, LifterLayerType type) throws IOException {
        gotoTargetLocation(taskId, new LifterLocationInfo(layer, type, LifterLocationType.INSIDE),
                LifterTaskProcess.GO_TO_END);
        int dis = lifterOper.getLifterDistance(layer, type);
        generalSafeTask(taskId, dis, LifterTaskType.LIFTER_TASK);
        sendTask(taskId);
    }

    public void generalSafeTask(int taskId, int endDis, LifterTaskType taskType) {
        if (container.getAllTasks().size() > 0) {
            return;
        }

        int startDis;
        if (taskType == LifterTaskType.LIFTER_TASK) {
            startDis = lifterOper.getLifterDis();
        } else if (taskType == LifterTaskType.CAR_TASK) {
            startDis = lifterOper.getTwoCarDis();
        } else if (taskType == LifterTaskType.PALLET_DOWN_TASK
            || taskType == LifterTaskType.PALLET_DOWN_TASK) {
            startDis = lifterOper.getTwoCarPalletDis();
        } else {
            throw new RuntimeException(lifterOper.getDeviceName() + " 任务类型错误");
        }

        container.generalTask(taskId, taskType, startDis, endDis, LifterTaskProcess.GO_TO_END);
    }

    /**
     * 移货
     * @param taskId
     * @param startLayer
     * @param startLocal
     * @param endLayer
     * @param endLocal
     * @throws IOException
     */
    public void transCargo(int taskId, int startLayer, LifterLocationType startLocal,
                           int endLayer, LifterLocationType endLocal) throws IOException {
        List<LifterTask> tasks = new ArrayList<>();
        //前往起点
        tasks.addAll(gotoTargetLocation(
                taskId, new LifterLocationInfo(startLayer, LifterLayerType.TWO_CAR_LAYER, startLocal),
                LifterTaskProcess.GO_TO_START));
        //取货
        tasks.add(sendPalletTask(taskId, PalletStatus.UP, LifterTaskProcess.GET_CARGO));
        //前往终点
        tasks.addAll(gotoTargetLocation(
                taskId, new LifterLocationInfo(endLayer, LifterLayerType.TWO_CAR_LAYER, endLocal),
                LifterTaskProcess.GO_TO_END));
        //放货
        tasks.add(sendPalletTask(taskId, PalletStatus.DOWN, LifterTaskProcess.PUT_CARGO));
        //回提升机内
        tasks.addAll(gotoTargetLocation(
                taskId, new LifterLocationInfo(endLayer, LifterLayerType.TWO_CAR_LAYER, LifterLocationType.INSIDE),
                LifterTaskProcess.BACK_LIFTER));
        sendTask(taskId);
        allTaskRun(taskId);
    }

    public List<LifterTask> gotoTargetLocation(Integer taskId, LifterLocationInfo target,
                                               LifterTaskProcess process) {
        List<LifterTask> tasks = new ArrayList<>();
        LifterLocationInfo locationInfo = lastLocation;
        if (locationInfo.equals(target)) {
            //目标点与当前点一致
            return tasks;
        }

        if (locationInfo.getLayerType() == null || locationInfo.getTwoCarLocation() == null) {
            throw new RuntimeException("当前没有两向车位置，无法执行任务");
        }

        if (locationInfo.getLayer() == target.getLayer() && locationInfo.getLayerType() == target.getLayerType()) {
            //目标层与小车当前层一致
            tasks.add(sendCarTask(taskId, locationInfo, target, process));
            return tasks;
        }

        //小车回提升机内
        tasks.addAll(gotoTargetLocation(taskId, new LifterLocationInfo(locationInfo.getLayer(), locationInfo.getLayerType(),
                LifterLocationType.INSIDE), process));

        //提升机换层
        tasks.add(sendLifterTask(taskId, locationInfo.getLayer(), locationInfo.getLayerType(),
                target.getLayer(), target.getLayerType(), process));

        if (target.getTwoCarLocation() != LifterLocationType.INSIDE) {
            //目标点不在提升机内
            tasks.add(sendCarTask(taskId,
                    new LifterLocationInfo(target.getLayer(), target.getLayerType(), LifterLocationType.INSIDE),
                    target, process));
        }

        return tasks;
    }

    /**
     * 发送两向车任务
     * @param taskId
     * @param locationInfo
     * @param target
     * @return
     * @throws IOException
     */
    private LifterTask sendCarTask(Integer taskId, LifterLocationInfo locationInfo, LifterLocationInfo target,
                                   LifterTaskProcess process) {
        int start = getCarDistance(locationInfo.getLayer(), locationInfo.getTwoCarLocation());
        int end = getCarDistance(locationInfo.getLayer(), target.getTwoCarLocation());
        LifterTask task = container.generalTask(taskId, LifterTaskType.CAR_TASK, start, end, process);
        lastLocation = target;
//        comm.sendTask(task.getTaskType(), task.getStart(), task.getEnd(), task.getTaskId());
        return task;
    }

    /**
     * 发送提升机任务
     * @param taskId
     * @param startLayer
     * @param startType
     * @param endLayer
     * @param endType
     * @return
     * @throws IOException
     */
    private LifterTask sendLifterTask(Integer taskId, int startLayer, LifterLayerType startType, int endLayer,
                                      LifterLayerType endType, LifterTaskProcess process) {
        int start = lifterOper.getLifterDistance(startLayer, startType);;
        int end = lifterOper.getLifterDistance(endLayer, endType);;
        LifterTask task = container.generalTask(taskId, LifterTaskType.LIFTER_TASK, start, end, process);
//        comm.sendTask(task.getTaskType(), task.getStart(), task.getEnd(), task.getTaskId());
        lastLocation = new LifterLocationInfo(endLayer, endType, LifterLocationType.INSIDE);
        return task;
    }

    /**
     * 只用于恢复提升机，提升机当前不可以有任务
     * @param taskId
     * @param endLayer
     * @param endType
     * @return
     * @throws IOException
     */
    private LifterTask sendLifterTask(Integer taskId, int endLayer, LifterLayerType endType,
                                      LifterTaskProcess process) throws IOException {
        if (lifterOper.isTasking() || lifterOper.isRuning()) {
            throw new RuntimeException("提升机运行中，当前提升机换层任务不可执行:任务id" + taskId);
        }

        int start = lifterOper.getLifterDis();
        int end = lifterOper.getLifterDistance(endLayer, endType);
        LifterTask task = container.generalTask(taskId, LifterTaskType.LIFTER_TASK, start, end, process);
//        comm.sendTask(task.getTaskType(), task.getStart(), task.getEnd(), task.getTaskId());
        lastLocation = new LifterLocationInfo(endLayer, endType, LifterLocationType.INSIDE);
        return task;
    }

    /**
     * 发送两向车托盘任务
     * @param taskId
     * @param endPalletStatus
     * @return
     * @throws IOException
     */
    public LifterTask sendPalletTask(Integer taskId, PalletStatus endPalletStatus,
                                     LifterTaskProcess process) {
        LifterTask task = null;
        switch (endPalletStatus) {
            case UP:
                int start = lifterOper.getPalletDistance(PalletStatus.DOWN);
                int end = lifterOper.getPalletDistance(PalletStatus.UP);
                task = container.generalTask(taskId, LifterTaskType.PALLET_UP_TASK, start, end, process);
                break;
            case DOWN:
                int start2 = lifterOper.getPalletDistance(PalletStatus.UP);
                int end2 = lifterOper.getPalletDistance(PalletStatus.DOWN);
                task = container.generalTask(taskId, LifterTaskType.PALLET_DOWN_TASK, start2, end2, process);
                break;
            default:
                throw new RuntimeException("不可执行该托盘任务:" + endPalletStatus);
        }
//        comm.sendTask(task.getTaskType(), task.getStart(), task.getEnd(), task.getTaskId());
        return task;
    }

    /**
     * 发送两向车托盘任务
     * @param taskId
     * @param endPalletStatus
     * @return
     * @throws IOException
     */
    public LifterTask sendPalletTaskOnly(Integer taskId, PalletStatus endPalletStatus,
                                     LifterTaskProcess process) {
        LifterTask task = null;
        switch (endPalletStatus) {
            case UP:
                int start = lifterOper.getTwoCarPalletDis();
                int end = lifterOper.getPalletDistance(PalletStatus.UP);
                task = container.generalTask(taskId, LifterTaskType.PALLET_UP_TASK, start, end, process);
                break;
            case DOWN:
                int start2 = lifterOper.getTwoCarPalletDis();
                int end2 = lifterOper.getPalletDistance(PalletStatus.DOWN);
                task = container.generalTask(taskId, LifterTaskType.PALLET_DOWN_TASK, start2, end2, process);
                break;
            default:
                throw new RuntimeException("不可执行该托盘任务:" + endPalletStatus);
        }
//        comm.sendTask(task.getTaskType(), task.getStart(), task.getEnd(), task.getTaskId());
        return task;
    }

    public void doSegment() {

    }

    public void clearTask() {

    }

    /**
     * 发送任务
     *
     * @param taskId
     */
    public void sendTask(int taskId) throws IOException {
        if (lifterOper.getWriteTaskNo() != 0) {
            throw new RuntimeException("提升机当前有任务，不能发送任务");
        }

        lastLocation = new LifterLocationInfo(lifterOper.getHoistCurrentLayer(), lifterOper.getHoistLayerType(),
                LifterLocationType.parseLocal(lifterOper.getTwoCarLocal()));
        List<LifterTask> tasks = container.getAllTasks();
        sendTask(taskId, tasks);
        LoggerUtil.info(ModuleType.FORK_LIFTER, LoggerType.TASK, taskId, lifterOper.getDeviceName(),
                "发送提升机整体任务:" + WcsFuncUtil.listToString(tasks), this.getClass());
    }

    /**
     * 发送任务
     *
     * @param taskId
     * @param tasks
     */
    public void sendTask(int taskId, List<LifterTask> tasks) throws IOException {
        if (tasks.size() > 30) {
            throw new RuntimeException("动作数最大只支持30个：" + tasks.size());
        }

        if (tasks.size() == 0) {
            throw new RuntimeException("当前没有任务可发送");
        }

        lifterOper.sendWholeTask(taskId, tasks);
    }

//    private void resetTaskEndLocation(List<LifterTask> tasks, LifterTask endTask) {
//        if (tasks.get(0).equals(endTask)) {
//            return;
//        }
//
//        int distance = endTask.getEnd();
//        switch (endTask.getTaskType()) {
//            case LIFTER_TASK:
//                lifterInfo.setTaskEndLocation(lifterInfo.getLifterLocationInfoByLifterDis(distance));
//                break;
//            case CAR_TASK:
//                resetTaskEndLocation(tasks, tasks.get(tasks.indexOf(endTask) - 1));
//                lifterInfo.setTaskEndLocation(
//                        lifterInfo.getLifterLocationInfoByCarDis(lifterInfo.getTaskEndLocation().getLayer(), distance));
//                break;
//            case PALLET_UP_TASK:
//            case PALLET_DOWN_TASK:
//                resetTaskEndLocation(tasks, tasks.get(tasks.indexOf(endTask) - 1));
//                break;
//            default:
//                throw new RuntimeException("任务类型错误，无法解析任务终点坐标");
//        }
//    }

    /**
     * 发送行驶任务
     * @param taskId
     * @return
     */
    public LifterCurSegmentInfo sendSegment(int taskId) throws IOException {
        if (lifterOper.getWriteCmdNo() != 0) {
            throw new RuntimeException("提升机指令编号不为0，无法发送行驶指令");
        }
        container.checkTaskId(taskId);
        int segment = container.getSegment();
        int cmdNo = container.getCmdNo();
        lifterOper.sendSegment(cmdNo, segment);
        LoggerUtil.info(ModuleType.FORK_LIFTER, LoggerType.TASK, taskId, lifterOper.getDeviceName(),
                "发送提升机行驶指令，指令号：" + cmdNo + ", 步数：" + segment, this.getClass());
        return new LifterCurSegmentInfo(cmdNo, container.getProcess());
    }

    public int allTaskRun(int taskId) throws IOException {
        if (lifterOper.getWriteCmdNo() != 0) {
            throw new RuntimeException("提升机当前有任务，不能发送任务");
        }
        container.checkTaskId(taskId);
        int segment = container.getAllTasks().size();
        int cmdNo = 100;
        lifterOper.sendSegment(cmdNo, segment);
        LoggerUtil.info(ModuleType.FORK_LIFTER, LoggerType.TASK, taskId, lifterOper.getDeviceName(),
                "发送提升机行驶指令，指令号：" + cmdNo + ", 步数：" + segment, this.getClass());
        return cmdNo;
    }

    /**
     * 当前行驶指令是否完成
     * @param taskId
     * @return
     */
    public boolean isTaskCmdEnd(int taskId, int cmdNo) {
        if (cmdNo != lifterOper.getFinishCmdNo()) {
            return false;
        }

        return container.isSegmentEnd(lifterOper.getFinishCmdNo(), taskId);
    }

    /**
     * 当前任务是否完成
     * @return
     */
    public boolean isTaskEnd(int taskId) {
        if (lifterOper.getHoistFinalTaskNo() != taskId) {
            return false;
        }

        return container.isTaskEnd(taskId);
    }

    public void lifterLocationReset() throws IOException {
        if (lifterOper.isArrived()) {
            LoggerUtil.info(ModuleType.FORK_LIFTER, LoggerType.INFO, null, lifterOper.getDeviceName(),
                    "提升机已到位，不用重新校准位置", this.getClass());
            return;
        }

        LifterTask task = new LifterTask(9800);

        if (!lifterOper.isCarArrived()) {
            task.setTaskType(LifterTaskType.CAR_TASK);
            task.setStart(lifterOper.getTwoCarDis());
            task.setTaskId(9800);
            task.setEnd(getCarDistance(lifterOper.getHoistCurrentLayer(),
                    LifterLocationType.parseLocal(lifterOper.getTwoCarLocal())));
            LoggerUtil.info(ModuleType.FORK_LIFTER, LoggerType.INFO, 9800, lifterOper.getDeviceName(),
                    "提升机故障恢复，两向车复位任务：" + task, this.getClass());
            sendTask(9800, Collections.singletonList(task));
            lifterOper.sendSegment(110,  1);
            checkLifterTaskEnd(9800, "故障恢复，两向车位置校准");
        }

//        if (!lifterInfo.isLifterArrived()) {
//            //提升机内有四向车则提升机不能去1层两向车层
//            if (!(lifterInfo.getCurrentLocation().getLayer() == 1 && lifterInfo.isHasFourCarSig()
//                    && lifterInfo.getCurrentLocation().getLayerType() == LifterLayerType.TWO_CAR_LAYER)) {
//                task.setTaskType(LifterTaskType.LIFTER_TASK);
//                task.setStart(lifterInfo.getCurLifterDis());
//                task.setEnd(lifterInfo.getLifterDistance(lifterInfo.getCurrentLocation().getLayer(),
//                        lifterInfo.getCurrentLocation().getLayerType()));
//                task.setTaskId(9799);
//                LifterLog.info(LoggerType.INFO, 9799, lifterInfo.getName(), "提升机故障恢复，提升机复位任务：" + task,
//                        this.getClass());
//                comm.sendTask(9799, Collections.singletonList(task));
//                comm.sendSegment(110,  1);
//                checkLifterTaskEnd(9799, "故障恢复，提升机位置校准");
//            }
//        }
    }

    private void checkLifterTaskEnd(int taskId, String des) throws IOException {
        int tick = 0;
        while (true) {
            if (lifterOper.getHoistFinalTaskNo() == taskId) {
                lifterOper.setHoistFinalTaskNoClear();
                lifterOper.clearLifterFinishTaskCmdNo();
                return ;
            }

            tick++;

            if (tick >= 20) {
                break;
            }

            if (lifterOper.getErrorCode() > 0) {
                break;
            }

            WcsFuncUtil.delay(1000);
        }

        throw new RuntimeException(des + "失败");
    }

    public int getCarDistance(int layer, LifterLocationType type) {
        if (type == LifterLocationType.INVALID_CAR_LOCATION) {
            return lifterOper.getTwoCarDis();
        }

        return lifterOper.getCarDistance(layer, type);
    }

    public int getCarDistance(Node node) {
        return lifterOper.getCarDistance(node);
    }

    public int getPalletDistance(PalletStatus palletStatus) {
        return lifterOper.getPalletDistance(palletStatus);
    }
}
